Mapper = function() {};

Mapper.prototype = {

  lon: 0,
  lat: 0,
  zoom: 2,
  updateme: true,
  map: null,
  wms: null,
  drawing: null,
  markers: null,
  mapdivnode: null,
  click: false,
  hover: null,
  uid: "map",
  ready: false,
  blockcommits: false,

  // add visual art
  add_point: function(lon,lat,descr,artwork) {

    if(!this.map || !lon || !lat || !descr) {
      return;
    }

    // xxx do better ... stand in artwork handling for now
    var artwork = "/appwiki/map/icons/emblem-favorite.png";
    if( descr.indexOf(":-)") == 0 )
      artwork = "/appwiki/map/icons/face-smile.png";
    else if( descr.indexOf(":-|") == 0 )
      artwork = "/appwiki/map/icons/face-plain.png";
    else if( descr.indexOf(":-(") == 0 )
      artwork = "/appwiki/map/icons/face-sad.png";
    else if( descr.indexOf(":-D") == 0 )
      artwork = "/appwiki/map/icons/face-smile-big.png";
    else if( descr.indexOf(":-o") == 0 )
      artwork = "/appwiki/map/icons/face-surprise.png";
    else if( descr.indexOf(";-)") == 0 )
      artwork = "/appwiki/map/icons/face-wink.png";
    else if( descr.indexOf(":-*") == 0 )
      artwork = "/appwiki/map/icons/face-kiss.png";
    else if( descr.indexOf(":-)") == 0 )
      artwork = "/appwiki/map/icons/face-smile.png";
    else if( descr.indexOf(":-)") == 0 )
      artwork = "/appwiki/map/icons/face-smile.png";
    else if( descr.indexOf(":-)") == 0 )
      artwork = "/appwiki/map/icons/face-smile.png";
    else if( descr.indexOf(":-)") == 0 )
      artwork = "/appwiki/map/icons/face-smile.png";

    var lonlat = new OpenLayers.LonLat(lon,lat);
    var textsz = new OpenLayers.Size(100,15);
    var iconsz = new OpenLayers.Size(32,32); 
    var icon   = new OpenLayers.Icon(artwork,iconsz);
    var marker = new OpenLayers.Marker(lonlat,icon);

    // openlayers stuff
    var xfocus = this;
    this.markers.addMarker(marker);
    marker.events.register("mouseover",marker, function() {
      if(!xfocus.hover) {
        xfocus.hover = new OpenLayers.Popup.Anchored("myuniqueid",
                             lonlat,
                             textsz,
                             descr
                            );
        //xfocus.hover = document.createElement("div");
        //xfocus.hover.style.position = 'absolute';
        //xfocus.hover.style.width = "100px";
        //xfocus.hover.style.height = "10px";
        //xfocus.hover.style.border = "2px solid red";
        //xfocus.mapdivnode.appendChild(xfocus.hover);
        xfocus.map.addPopup(xfocus.hover);
      } else {
      }
      //xfocus.hover.style.display = "visible";
      //xfocus.hover.style.left = "40px";
      //xfocus.hover.style.top = "50px";
      //xfocus.hover.style.innerHTML = "a marker";
    } );

    // more openlayers stuff
    marker.events.register("mouseout",marker,function() {
      if(xfocus.hover) {
        xfocus.map.removePopup(xfocus.hover);
        xfocus.hover = null; // xxx something horrible if try to reuse
        //xfocus.hover.style.display = "none";
      }
    } );

    // all done
  },


  // listen for interesting db events for other unrelated database activity
  // xxx it is arguable that this should mostly be done by a smarter server.
  database_listen: function(e) {

    // look at certain kinds of things only
    if(!this || !this.map || !this.ready || !e || !e.title) {
      return;
    }

    // go somewhere; may geocode
    if( e.title.indexOf('/map goto')==0 && e.handler=='chat' ) {
      // xxx write this; should do on server or client?
      // xxx may want to start moving to server here
      return;
    }

    // store a mappable object if a comment requests it
    if( e.title.indexOf('/map')==0 && e.handler=='chat' ) {

      // touch the item with the map current focus
      var center = this.map.getCenter();
      e.lat = center.lat.toString();
      e.lon = center.lon.toString();

      // store our own copy
      DatabaseStore(this,this.uid+"/#",
              {title:e.title,handler:'marker',lat:e.lat,lon:e.lon} );
      return;
    }

  },

  // when an area has no content; just do nothing in particular
  database_update_empty: function(e) {
    if(this.updateme && this.ready) {
      this.lon = 0;
      this.lat = 0;
      this.zoom = 1;
      //alert('defaulting to ' + this.lon + " " + this.lat + " " + this.zoom );
      this.map.events.remove("moveend");
      this.map.setCenter(new OpenLayers.LonLat(this.lon, this.lat), this.zoom);
      this.map.events.register("moveend", this, this.event_moved );
      this.updateme = false;
    }
  },

  // update display with new item
  database_update: function(e) {

    // paranoia
    if(!this || !this.ready || !e) return;

    // state change request? xxx this will go away and state will be in map parent node
    if(e.handler == "state") {
      this.lat = e.lat;
      this.lon = e.lon;
      this.zoom = e.zoom;
      this.updateme = true;
    }
    // clumsy insertion of vector features for now xxx
    else if(e.kind == "Polygon") {
      var parts = e.geometry.split(" ");
      var points = [];
      for(var i=0;i<parts.length-2;i+=2) {
        var p = new OpenLayers.Geometry.Point(parseFloat(parts[i+1]),parseFloat(parts[i]));
        points.push(p);
      }
      var linear_ring = new OpenLayers.Geometry.LinearRing(points);
      var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Polygon([linear_ring]));
      this.blockcommits = true;
      this.drawing.addFeatures([feature]);
      this.updateme = true;
      this.blockcommits = false;
    }
    // may be a point marker? xxx improve as well
    else if(e.handler == "marker"){
      this.add_point(e.lon,e.lat,e.title,e.artwork);
    }
    // error or perhaps the map itself { server sends the parent of any request as well }
    else {
      
    }

    // update latched?
    // this also handles the first refresh so that the map starts off on without a flicker
    if(this.updateme) {
      //alert('moving to ' + this.lon + " " + this.lat + " " + this.zoom );
      this.map.events.remove("moveend");
      this.map.setCenter(new OpenLayers.LonLat(this.lon, this.lat), this.zoom);
      this.map.events.register("moveend", this, this.event_moved );
      this.updateme = false;
    }
  },

  // handle map moved
  event_moved: function() {
    if(!this || !this.ready) return;
    if(this.blockcommits) return;
    var center = this.map.getCenter();
    var zoom = this.map.getZoom();
    if(center.lat && center.lon && zoom) {
      var f = new Object();
      f.handler = "state";
      f.lat = center.lat.toString();
      f.lon = center.lon.toString();
      f.zoom = zoom;
      f.title = "/map goto " + f.lat + " " + f.lon + " " + f.zoom;
      DatabaseStore(this,"state",f);
    }
  },

  // start
  startup: function(node) {

    // where to store posts
    this.uid = "map";

    // make map 
    // do not bother to center the map yet; wait for database sync
    this.mapdivnode = node;
    this.map = new OpenLayers.Map( node ); //, { controls:[] } );

    this.wms = new OpenLayers.Layer.WMS( "OpenLayers WMS",
                        "http://labs.metacarta.com/wms-c/Basic.py?",
                        {layers: 'basic' , format: 'image/png' } );
    this.markers = new OpenLayers.Layer.Markers("Some points of interest");
    this.drawing = new OpenLayers.Layer.Vector("drawing");
    this.map.addLayers([this.wms,this.markers,this.drawing]);
    // do not bother to center the map yet; wait till database sync

    // load some map styling
    var oLink = document.createElement("link")
    oLink.href = "/appwiki/map/map.css";
    oLink.rel = "stylesheet";
    oLink.type = "text/css";
    document.body.appendChild(oLink);

    // add editing
    {
            this.zb = new OpenLayers.Control.ZoomBox();
            this.panel = new OpenLayers.Control.Panel({defaultControl: this.zb});
            this.panel.addControls([
                new OpenLayers.Control.MouseDefaults(),
                this.zb,
//                new OpenLayers.Control.DrawFeature(map.drawing, OpenLayers.Handler.Path),
                new OpenLayers.Control.ZoomToMaxExtent()
            ]);
            this.map.addControl(this.panel);
    }

/*
        this.addControls(
          [ new OpenLayers.Control.Navigation() ]
        );  
        var controls = [
          new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Point, {'displayClass': 'olControlDrawFeaturePoint'}),
          new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Path, {'displayClass': 'olControlDrawFeaturePath'}),
          new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Polygon, {'displayClass': 'olControlDrawFeaturePolygon'})
        ];
        for (var i = 0; i < controls.length; i++) {
            controls[i].featureAdded = function(feature) { feature.state = OpenLayers.State.INSERT; }
        }
*/

    // listen to feature additions to map to save them
    var localscope = this;
    this.drawing.onFeatureInsert = function(feature) {
      if(localscope.blockcommits == false) {
        var writer = new OpenLayers.Format.JavascriptObject({},localscope.drawing);
        var temp = new Array();
        temp.push(feature); // same as localscope.drawings.features.last
        var data = writer.write(temp);
        data[0].handler = "vector";
        DatabaseStore(localscope,localscope.uid+"/#",data[0]);
      }
    }

    // be ready
    this.ready = true;

    // listen to map drag end so we can persist it to the server
    this.map.events.register("moveend", this, this.event_moved );

    // listen to local database events
    DatabaseListen(this);

    // specifically get the location
    DatabaseGet(this,"state","handler=state");

    // start synchronizing with the database ( it calls back to database_update() here )
    DatabaseSubscribe( this, 'map' );

    return this;
  },

  // stop
  shutdown: function() {
  }

}

Mapper.make = function(arg) {
  return (new Mapper()).startup(arg);
}
